/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;

namespace Cobalt.Content.Pipeline.Graphics
{
	public class GeometrySurfaceContent : ContentItem
	{
		public GeometrySurfaceContent()
		{
		}

		private GeometryContent source;
		public GeometryContent Source
		{
			get { return source; }
			set { source = value; }
		}

		private MaterialContent material;
		public MaterialContent Material
		{
			get { return material; }
			set { material = value; }
		}

		private int vertexCount;
		public int VertexCount
		{
			get { return vertexCount; }
			set { vertexCount = value; }
		}

		private int firstVertex;
		public int FirstVertex
		{
			get { return firstVertex; }
			set { firstVertex = value; }
		}

		private int indexCount;
		public int IndexCount
		{
			get { return indexCount; }
			set { indexCount = value; }
		}

		private int firstIndex;
		public int FirstIndex
		{
			get { return firstIndex; }
			set { firstIndex = value; }
		}

		private BoundingSphere bounds;
		public BoundingSphere Bounds
		{
			get { return bounds; }
			set { bounds = value; }
		}
	}

	public partial class GeometryBufferContent : ContentItem
	{
		public GeometryBufferContent()
		{
		}

		private VertexBufferContent vertexBuffer;
		public VertexBufferContent VertexBuffer
		{
			get { return vertexBuffer; }
			set { vertexBuffer = value; }
		}

		private VertexElement[] vertexDeclaration;
		public VertexElement[] VertexDeclaration
		{
			get { return vertexDeclaration; }
			set { vertexDeclaration = value; }
		}

		private IndexCollection indices;
		public IndexCollection Indices
		{
			get { return indices; }
			set { indices = value; }
		}

		private List<GeometrySurfaceContent> surfaces = new List<GeometrySurfaceContent>();
		public List<GeometrySurfaceContent> Surfaces { get { return surfaces; } }
	}

	public class GeometryBufferContentCollection : Collection<GeometryBufferContent>
	{
		public GeometryBufferContentCollection() { }
		public void AddRange( IEnumerable<GeometryBufferContent> items )
		{
			if( items == null )
				throw new ArgumentNullException( "items" );

			foreach( GeometryBufferContent buf in items )
				Add( buf );
		}

		public int GetSurfaceIndex( GeometrySurfaceContent surface )
		{
			int nSurfs = 0;
			for( int i = 0; i < Count; i++ )
			{
				GeometryBufferContent buffer = this[i];
				for( int j = 0; j < buffer.Surfaces.Count; j++ )
				{
					if( buffer.Surfaces[j] == surface )
						return nSurfs + j;
				}
				nSurfs += buffer.Surfaces.Count;
			}

			return -1;
		}

		public int GetSurfaceIndex( GeometryContent surfaceSource )
		{
			int nSurfs = 0;
			for( int i = 0; i < Count; i++ )
			{
				GeometryBufferContent buffer = this[i];
				for( int j = 0; j < buffer.Surfaces.Count; j++ )
				{
					if( buffer.Surfaces[j].Source == surfaceSource )
						return nSurfs + j;
				}
				nSurfs += buffer.Surfaces.Count;
			}

			return -1;
		}
	}

	[ContentTypeWriter]
	public class StaticGeometryWriter : ContentTypeWriter<GeometryBufferContentCollection>
	{
		protected override void Write( ContentWriter output, GeometryBufferContentCollection value )
		{
			List<MaterialContent> materials = new List<MaterialContent>();
			Dictionary<MaterialContent, int> materialMap = new Dictionary<MaterialContent, int>();

			foreach( GeometryBufferContent buffer in value )
			{
				foreach( GeometrySurfaceContent surf in buffer.Surfaces )
				{
					if( surf.Material == null ||
						!(surf.Material is CobaltMaterialContent ||
						surf.Material is ExternalCobaltMaterialContent ||
						surf.Material is NamedCobaltMaterialContent) )
						throw new InvalidContentException( string.Format( "Invalid surface material '{0}'.", surf.Material ), surf.Identity );

					if( !materialMap.ContainsKey( surf.Material ) )
					{
						materials.Add( surf.Material );
						materialMap.Add( surf.Material, materials.Count - 1 );
					}
				}
			}

			output.Write( materials.Count );
			foreach( MaterialContent mat in materials )
				output.WriteObject( mat );

			output.Write( value.Count );
			foreach( GeometryBufferContent buffer in value )
			{
				output.WriteObject( buffer.VertexBuffer );
				output.WriteObject( buffer.VertexDeclaration );
				output.WriteObject( buffer.Indices );

				output.Write( buffer.Surfaces.Count );
				foreach( GeometrySurfaceContent surf in buffer.Surfaces )
				{
					output.Write( materialMap[surf.Material] );
					output.WriteRawObject( surf.Bounds );

					output.Write( surf.FirstVertex );
					output.Write( surf.VertexCount );
					output.Write( surf.FirstIndex );
					output.Write( surf.IndexCount / 3 ); //PrimitiveCount
				}
			}
		}

		public override string GetRuntimeReader( TargetPlatform targetPlatform )
		{
			return Helpers.GetCobaltFrameworkTypeString( "Graphics.StaticGeometryReader" );
		}

		public override string GetRuntimeType( TargetPlatform targetPlatform )
		{
			return Helpers.GetCobaltFrameworkTypeString( "Graphics.StaticGeometry" );
		}
	}
}